from typing import List
from bpy.types import Object


def check_if_have_previous_kf(ob:Object, frame:int, dpath:str) -> bool:
    if not ob.animation_data or not ob.animation_data.action or not ob.animation_data.action.fcurves:
        return False

    pre_anim = ob.animation_data.action.fcurves.find(dpath)
    if pre_anim:
        prev_keyframes = pre_anim.keyframe_points
        return next((True for kp in prev_keyframes if kp.co.x == frame), False)
        # return any(kp.co.x == frame for kp in prev_keyframes)

    return False


def remove_keyframe_x(dpath:str, objects:List[Object], frame_target:int) -> list:

    """ Elimina el frame especifico frame_target de la curva, del data path indicado a los objetos del listado """

    axis = 0 # X

    altered_obs = set()

    for ob in objects:
        if not ob.animation_data:
            continue

        action = ob.animation_data.action
        if not action:
            continue

        if not ob.animation_data.action.fcurves:
            continue

        fcurve = action.fcurves.find(data_path=dpath, index=axis)
        if not fcurve:
            continue

        # Con esta manera de eliminar keyframes, parece que siempre 
        # tiene que existir un ultimo keyframe (sin ser borrado) en la curva:
        
        for p in fcurve.keyframe_points:
            if int(p.co.x) == frame_target:
                fcurve.keyframe_points.remove(p)
            
        # Si solo queda un solo keyframe en la curva la borramos por completo:
        if len(fcurve.keyframe_points) == 1:
            action.fcurves.remove(fcurve)
        
        altered_obs.add(ob)
    
    return list(altered_obs)


def anim_attrs(data:dict) -> None:

    """ 
        Usage:
        data =  {
                    'object':  p_settigns, 
                    'dpath':   "damping", 
                    'frame':   10,
                    'value':   1
        }
    """

    ob = data['object']
    dpath = data['dpath']
    frame = data['frame']
    value = data['value']
    
    setattr(ob, dpath, value)
    ob.keyframe_insert(data_path=dpath, frame=frame)


# UI Animation functions
#------------------------------------------------------------------------------------------------------------------------

def draw_anim_props(layout, type_props:str, props, key_prop_name:str, buttons_op:str) -> None:

    # inicialmente diseñado para Fluid Settings Animation, pero también lo estoy reutilizando en Colliders Animations.

    flow = layout.grid_flow(row_major=True, columns=2, even_columns=True, even_rows=False, align=True)
    scale_x = 3.2
    scale_y = 1.3

    exceptions = ["use_viscoelastic_springs", "enable_disable"]

    if key_prop_name not in exceptions:

        lab = flow.row(align=True)
        lab.alignment = 'RIGHT'
        lab.label(text="Start | Duration")
        row = flow.row(align=True)
        row.prop(props, key_prop_name + "_start", text="")
        row.prop(props, key_prop_name + "_duration", text="")

        # Increase Decrease Opcional:
        strength_inc_dec = getattr(props, key_prop_name + "_inc_dec")
        
        lab = flow.row(align=True)
        lab.alignment = 'RIGHT'
        lab.label(text="")
        incdec = flow.row(align=True)
        
        btincdec = incdec.row(align=True)
        btincdec.use_property_split = False
        btincdec.scale_x = 9
        btincdec.prop(props, key_prop_name + "_inc_dec", text="Increase Decrease", toggle=True)
        incdec.prop(props, key_prop_name + "_inc_dec", text="", toggle=True, icon='TRIA_DOWN' if strength_inc_dec else 'TRIA_LEFT')

        if strength_inc_dec:
            lab = flow.row(align=True)
            lab.alignment = 'RIGHT'
            lab.label(text="Increase | Decrease")
            row = flow.row(align=True)
            row.prop(props, key_prop_name + "_increase", text="")
            row.prop(props, key_prop_name + "_decrease", text="")
        
        lab = flow.row(align=True)
        lab.alignment = 'RIGHT'
        lab.label(text="From | To")
        row = flow.row(align=True)
        row.prop(props, key_prop_name + "_from", text="")
        row.prop(props, key_prop_name + "_to", text="")
    
    else: # excepciones use_viscoelastic_springs(Fluid Settigns) y enable_disable(colliders):
        
        viscoflow = layout.grid_flow(row_major=True, columns=2, even_columns=True, even_rows=False, align=True)
        viscoflow.alignment = 'RIGHT'

        lb = viscoflow.row(align=True)
        lb.alignment = 'RIGHT'
        lb.scale_x = 9.21
        lb.label(text="Switch")
        
        switch = viscoflow.row(align=True)
        switch.alignment = 'RIGHT'
        switch.scale_x = 4.8
        switch.use_property_split = False

        switch.prop(props, key_prop_name + "_from_to", expand=True, toggle=True)
        
        frame_label = viscoflow.row(align=True)
        frame_label.use_property_split = False
        frame_label.scale_x = 8
        frame_label.alignment = 'RIGHT'
        frame_label.label(text="Frame")
        frame_value = viscoflow.row(align=True)
        frame_value.scale_x = 6
        frame_value.prop(props, key_prop_name + "_start", text="")
    
    layout.separator()

    # BOTONES:

    buttons = layout.grid_flow(row_major=True, columns=3, even_columns=False, even_rows=False, align=True)
    animed = getattr(props, key_prop_name + "_animed")
    buttons.scale_x = 0.77 # para rectificar los tamaños en x por el "even_columns=True" del flow
    if not animed:
        buttons.label(text="")
        bt_add = buttons.row(align=True)
        bt_add.scale_y = scale_y
        bt_add.scale_x = 1.8
        bt_add.operator(buttons_op, text="Add", icon='KEY_HLT').call_from = "Add" + "." + type_props + "." + key_prop_name
    else:
        scale_x = 0.96
        buttons.label(text="")
        bt_update = buttons.row(align=True)
        bt_update.scale_y = scale_y
        bt_update.scale_x = scale_x
        bt_update.operator(buttons_op, text="Update", icon='FILE_REFRESH').call_from = "Update" + "." + type_props + "." + key_prop_name
        bt_remove = buttons.row(align=True)
        bt_remove.scale_y = scale_y
        bt_remove.scale_x = scale_x
        bt_remove.alert = True
        bt_remove.operator(buttons_op, text="Remove", icon='KEY_DEHLT').call_from = "Remove" + "." + type_props + "." + key_prop_name
